LIBRARY ieee;
USE ieee.std_logic_1164.all;
USE ieee.numeric_std.all;

-- TODO List:
-- Add Broadcast functionality
-- Add VLAN (broadcast limiting)
ENTITY fabric IS
      PORT ( Clock 		: 	IN      STD_LOGIC ;
			 RESETN		:	IN		STD_LOGIC ;
			 Bcast		:	IN		STD_LOGIC ;
			 PMDataValid :	IN		STD_LOGIC ; 	-- port map block has data ready
			
			 Ptr		: 	IN	  	STD_LOGIC_VECTOR (8 DOWNTO 0) ;
			 PortDestin :	IN		STD_LOGIC_VECTOR (1 DOWNTO 0) ;	
			 PortSrc	:	IN		STD_LOGIC_VECTOR (1 DOWNTO 0) ;
			 VlanTag	:	IN		STD_LOGIC_VECTOR (31 DOWNTO 0);
			 TagValid	:	IN		STD_LOGIC ;
					
			 XmtReady	:	IN		STD_LOGIC ;		-- xmt block 1 ready to receive data 
						
			 Ready		:	OUT		STD_LOGIC ;		-- ready for port map block
			 DataValid1 :	OUT		STD_LOGIC ;		-- data ready for xmt block 1
			 DataValid2 :	OUT		STD_LOGIC ;		-- data ready for xmt 2
			 DataValid3 :	OUT		STD_LOGIC ;		-- "		"	  xmt 3
			 Drop		:	OUT		STD_LOGIC ;		-- data valid for drop block
			 PtrOut		:	OUT		STD_LOGIC_VECTOR (8 DOWNTO 0) ;	-- pointer going to xmt block 1
			 VlanTagOut	:	OUT		STD_LOGIC_VECTOR (31 DOWNTO 0) ;	-- VLAN Tag to be inserted (block 1)
			 TagValidOut:	OUT		STD_LOGIC 		-- add tag, re-calc CRC (1)
			);
END fabric;

ARCHITECTURE Behavior OF fabric IS
	TYPE State_type IS (RESET, WAITFORNEW, TESTBCAST, WaitForXmtBcast, WaitForXmtDrop, WaitForXmtSingle, OUTPUT );
	SIGNAL y : State_type := WAITFORNEW;
	SIGNAL	 M_Bcast	:	STD_LOGIC ;		-- local broadcast designation
	SIGNAL	 M_Ptr		:	STD_LOGIC_VECTOR (8 DOWNTO 0) ; -- local frame pointer
	SIGNAL	 M_PortDest :	STD_LOGIC_VECTOR (1 DOWNTO 0) ;	-- local port destination
	SIGNAL	 M_PortSrc	:	STD_LOGIC_VECTOR (1 DOWNTO 0) ; -- local port soruce

BEGIN
	-- Initialize

	PROCESS (Clock, RESETN)
	VARIABLE suppressBcast	: STD_LOGIC_VECTOR (2 DOWNTO 0) := "000" ; -- represents ports to suppress on broadcasts
	VARIABLE toBcast	 	: STD_LOGIC_VECTOR (2 DOWNTO 0) ; -- ports yet to be broadcast to
	BEGIN
		IF (Clock'EVENT AND Clock = '1') THEN
			IF (RESETN = '1') THEN
				y <= RESET;
			ELSE
				CASE y IS
				
					WHEN RESET =>
						--Not sending any data
						DataValid1	<= '0';
						DataValid2	<= '0';
						DataValid3	<= '0';
						Drop		<= '0';
						Ready		<= '1';
						IF PMDataValid = '0' THEN
							y <= WAITFORNEW;
						END IF;
						
					WHEN WAITFORNEW =>
						--Not sending any data
						DataValid1	<= '0';
						DataValid2	<= '0';
						DataValid3	<= '0';
						Drop		<= '0';
						Ready		<= '1';
						
						IF PMDataValid = '1' THEN
							M_Bcast 	<= Bcast;
							M_Ptr		<= Ptr;
							M_PortDest	<= PortDestin;
							M_PortSrc	<= PortSrc;
							Ready		<= '0';
							
							y			<= TESTBCAST;
						END IF;
						
					WHEN TESTBCAST =>
							IF M_Bcast = '1' THEN
								CASE M_PortSrc IS
									WHEN "01" =>
										toBcast := "110";
									WHEN "10" =>
										toBcast := "101";
									WHEN "11" =>
										toBcast := "011";
									WHEN "00" =>
										toBcast := "111";
								END CASE;
								--toBcast := "111";
								--IF NOT (M_PortSrc = "00") THEN
								--	toBcast(TO_INTEGER(UNSIGNED(M_PortSrc))-1) := '0';
								--END IF;
								toBcast := toBcast AND NOT suppressBcast;	-- remove ports suppressed by VLAN
								
								IF (XmtReady = '1') THEN
									DataValid1 <= toBcast(0);
									DataValid2 <= toBcast(1);
									DataValid3 <= toBcast(2);

									IF (toBcast = "000") THEN	-- no broadcasting employed
										Drop <= '1';
									END IF;
									
									y <= OUTPUT;
								ELSE
									y <= WaitForXmtBcast;
								END IF;

							ELSIF M_PortDest = M_PortSrc THEN
								IF XmtReady = '1' THEN	
									Drop <= '1';
									y <= OUTPUT;
								ELSE
									y <= WaitForXmtDrop;
								END IF;
							ELSIF M_PortDest = "01" THEN
								IF XmtReady = '1' THEN	
									DataValid1 <= '1';
									y <= OUTPUT;
								ELSE
									y <= WaitForXmtSingle;
								END IF;
							ELSIF M_PortDest = "10" THEN
								IF XmtReady = '1' THEN	
									DataValid2 <= '1';
									y <= OUTPUT;
								ELSE
									y <= WaitForXmtSingle;
								END IF;
							ELSIF M_PortDest = "11" THEN
								IF XmtReady = '1' THEN	
									DataValid3 <= '1';
									y <= OUTPUT;
								ELSE
									y <= WaitForXmtSingle;
								END IF;
							END IF;

					WHEN WaitForXmtBcast =>
						IF M_Bcast = '1' THEN
							IF (XmtReady = '1') THEN
								DataValid1 <= toBcast(0);
								DataValid2 <= toBcast(1);
								DataValid3 <= toBcast(2);

								IF (toBcast = "000") THEN	-- no broadcasting employed
									Drop <= '1';
								END IF;
								
								y <= OUTPUT;
							END IF;
						END IF;
						
					WHEN WaitForXmtDrop =>
						IF XmtReady = '1' THEN
							Drop <= '1';
							y <= OUTPUT;
						END IF;
						
					WHEN WaitForXmtSingle =>
							IF M_PortDest = "01" THEN
								IF XmtReady = '1' THEN	
									DataValid1 <= '1';
									y <= OUTPUT;
								END IF;
							ELSIF M_PortDest = "10" THEN
								IF XmtReady = '1' THEN	
									DataValid2 <= '1';
									y <= OUTPUT;
								END IF;
							ELSIF M_PortDest = "11" THEN
								IF XmtReady = '1' THEN	
									DataValid3 <= '1';
									y <= OUTPUT;
								END IF;
							END IF;
					
					WHEN OUTPUT =>
						PtrOut(8 DOWNTO 1) <= M_Ptr(8 DOWNTO 1);
						PtrOut(0) <= M_Bcast;
						VlanTagOut <= VlanTag;
						TagValidOut <= TagValid;
						DataValid1 <= '0';
						DataValid2 <= '0';
						DataValid3 <= '0';
						Drop <= '0';
						y <= RESET;
						
												
--					WHEN BROADCAST =>
--						DataValid1 <= '0';
--						DataValid2 <= '0';
--						DataValid3 <= '0';
--						IF toBcast(0) = '1' AND Xmt1Ready = '1' THEN
---							PtrOut1 <= M_Ptr;
--							DataValid1 <= '1';
--							VlanTag_1 <= VlanTag;
--							TagValid1 <= TagValid;
--							toBcast(0) := '0';
--						END IF;
--						IF toBcast(1) = '1' AND Xmt2Ready = '1' THEN
--							PtrOut2 <= M_Ptr;
--							DataValid2 <= '1';
--							VlanTag_2 <= VlanTag;
--							TagValid2 <= TagValid;
--							toBcast(1) := '0';
--						END IF;
--						IF toBcast(2) = '1' AND Xmt3Ready = '1' THEN
--							PtrOut3 <= M_Ptr;
--							DataValid3 <= '1';
--							VlanTag_3 <= VlanTag;
--							TagValid3 <= TagValid;
--							toBcast(2) := '0';
--						END IF;
--						IF toBcast = "000" THEN
--							IF PMDataValid = '1' THEN
--								y <= WAITFORNEW;
--							ELSE
--								y <= RESET;
--							END IF;
--						END IF;
--										
---					WHEN SINGLE =>
--						IF M_PortDest = M_PortSrc THEN
--							IF DropReady = '1' THEN
--								PtrDrop <= M_Ptr;
--								DV_Drop <= '1';
--								IF PMDataValid = '1' THEN
--									y <= WAITFORNEW;
--								ELSE
--									y <= RESET;
--								END IF;
--							END IF;
--						ELSIF M_PortDest = "01" THEN
---							IF Xmt1Ready = '1' THEN
--								PtrOut1 <= M_Ptr;
--								DataValid1 <= '1';
--								VlanTag_1 <= VlanTag;
--								TagValid1 <= TagValid;
---								IF PMDataValid = '1' THEN
--									y <= WAITFORNEW;
--								ELSE
--									y <= RESET;
--								END IF;
--							END IF;
---						ELSIF M_PortDest = "10" THEN
--							IF Xmt2Ready = '1' THEN
--								PtrOut2 <= M_Ptr;
--								DataValid2 <= '1';
--								VlanTag_2 <= VlanTag;
--								TagValid2 <= TagValid;
--								IF PMDataValid = '1' THEN
--									y <= WAITFORNEW;
--								ELSE
--									y <= RESET;
--								END IF;
--							END IF;
---						ELSIF M_PortDest = "11" THEN
--							IF Xmt3Ready = '1' THEN
--								PtrOut3 <= M_Ptr;
--								DataValid3 <= '1';
--								VlanTag_3 <= VlanTag;
--								TagValid3 <= TagValid;
---								IF PMDataValid = '1' THEN
--									y <= WAITFORNEW;
--								ELSE
--									y <= RESET;
--								END IF;
--							END IF;
--						END IF; -- if for destination port

						
					END CASE; -- case for state (y)
				END IF;
		END IF ; -- clock edge
	END PROCESS ;
END Behavior ;